﻿using System;
using System.Collections.Generic;
using System.Reflection;
using System.Text;

namespace rehot.core
{
    using SanlieHash;

    public class HotMethods
    {
        internal HotType _hottype { get; set; }
        internal Dictionary<string, MethodInfo> _methods { get; set; }
        internal Dictionary<string, StringBuilder> _methods_name { get; set; }
        internal Dictionary<string, string> _methods_alias { get; set; }
        internal void Lose()
        {
            if (_methods != null)_methods.Clear();
            if (_methods_name != null)_methods_name.Clear();
            if (_methods_alias != null) _methods_alias.Clear();
            _hottype = null;
            _methods = null;
            _methods_name = null;
            _methods_alias = null;
        }
        public HotMethods(HotType hotType)
        {
            this._hottype = hotType;
            var runtime_methods = this._hottype._type.GetMethods();
            this._methods = new Dictionary<string, MethodInfo>(runtime_methods.Length);
            this._methods_name = new Dictionary<string, StringBuilder>(runtime_methods.Length);
            this._methods_alias = new Dictionary<string, string>(runtime_methods.Length);
            string id, name_id, args_snapshot;
            foreach (var method in runtime_methods)
            {
                args_snapshot = read_params_snapshot(method);
                name_id = FastHash.GetSM3(method.Name);
                id = FastHash.GetSM3(method.Name+ args_snapshot);

                if (!this._methods_name.ContainsKey(name_id)) this._methods_name.Add(name_id, new StringBuilder(args_snapshot));
                else this._methods_name[name_id].Append("\n").Append(args_snapshot);
                if (!this._methods.ContainsKey(id)) this._methods.Add(id, method);
                else if (HasOverride(method)) this._methods[id] = method; // override 覆写
                else if ((method.Attributes & MethodAttributes.NewSlot) != 0) this._methods[id] = method;  // new 方法则覆盖
            }
        }
        internal static bool HasOverride(MethodInfo method)
        {
            return method.GetBaseDefinition().DeclaringType != method.DeclaringType;
        }
        internal static string read_params_snapshot(MethodInfo method_info)
        {
            var _args_type = method_info.GetParameters();
            var _snapshot = new StringBuilder("(");
            for (int i = 0; i < _args_type.Length; i++) _snapshot.Append(_args_type[i].ParameterType.Name).Append(" ,");
            if (_snapshot.Length > 2) _snapshot.Remove(_snapshot.Length - 2, 2);
            return _snapshot.Append(")").ToString().ToLower();
        }
        public bool Exist(string name)
        {
            return _methods_name.ContainsKey(FastHash.GetSM3(name));
        }
        public bool Exist(string name, params IHotParam[] _params)
        {
            return !string.IsNullOrEmpty(FindMethodId(name, _params));
        }
        public IEnumerable<string> AllMethodId()
        {
            foreach (var key in _methods.Keys) yield return key;
        }
        public string ReadNameById(string id)
        {
            if (_methods.ContainsKey(id)) return _methods[id].Name;
            return string.Empty;
        }
        public string FindMethodId(string name, params IHotParam[] _params)
        {
            var param_snapshot = HotParam.ReadParamTypeSnapshot(_params);
            var id = FastHash.GetSM3(name + param_snapshot);
            if (_methods.ContainsKey(id)) return id;
            if (_methods_alias.ContainsKey(id)) return _methods_alias[id];

            var _id = FastHash.GetSM3(name);
            int i;
            if (_methods_name.ContainsKey(_id))
            {
                var params_count = _params.Length;
                foreach (var method in _methods_name[_id].ToString().Split('\n'))
                {
                    var _items = method.Trim('(', ')').Replace(" ,", "\n").Split('\n');
                    if (params_count != _items.Length) continue;
                    for (i = 0; i < _params.Length; i++) if (!_params[i].IsTypeName(_items[i])) break;

                    if (i != _params.Length) continue;
                    _id = FastHash.GetSM3(name + method);
                    if (_methods.ContainsKey(_id))
                    {
                        _methods_alias.Add(id, _id); //缓存
                        return _id;
                    }
                    break;
                }
            }
            return string.Empty;
        }
        public object CallByMethodId(object obj, string id, params IHotParam[] _params)
        {
            if (!_methods.ContainsKey(id)) throw new EntryPointNotFoundException(id);
            if (obj == null && !_methods[id].IsStatic) throw new ArgumentNullException(id); //非静态需要传入实例

            return _methods[id].Invoke(obj, HotParam.Read(_params));
        }
        public Tout CallByMethodId<Tout>(object obj, string id, params IHotParam[] _params)
        {
            var rlt = CallByMethodId(obj, id, _params);
            if (rlt is Tout rlt1) return rlt1;
            return default;
        }
        public HotObject CallUnknownByMethodId(object obj, string id, params IHotParam[] _params)
        {
            var rlt = CallByMethodId(obj, id, _params);
            if (rlt == null) return HotObject.Null;
            return new HotObject(rlt);
        }
        public object Call(object obj, string name, params IHotParam[] _params)
        {
            var id = FindMethodId(name, _params);
            return CallByMethodId(obj, id, _params);
        }
        public Tout Call<Tout>(object obj, string name, params IHotParam[] _params)
        {
            var id = FindMethodId(name, _params);
            return CallByMethodId<Tout>(obj, id, _params);
        }
        public HotObject CallUnknown(object obj, string name, params IHotParam[] _params)
        {
            var id = FindMethodId(name, _params);
            return CallUnknownByMethodId(obj, id, _params);
        }
    }
}
